/*

 ___col___
/         \
+---------+ \
| + + + + | | row
| + + + + | |
+---------+ /

*/

#pragma once
#include <cmath>
#include <vector>
#include <stdexcept>
namespace qing {
template <typename T>
class Matrx {  /* 矩阵类 - 二维向量 */
public:
    Matrx(long col, long row): col(col), row(row) {  /* 该构造函数用于从头开始构造 */
        arr = std::vector<T>(col * row);
    }
    Matrx(std::vector<T>& src) {  /* 该构造函数用于通过已有的vector进行构造 */
        arr = src;
    }
    T& get(long col, long row) {  /* 访问器 - 访问矩阵元素 */
        return arr[row * this->col + col];
    }
    std::vector<T> get_data() {  /* 访问器 - 用于获取原始数据 */
        return arr;
    }
    long get_col() {  /* 获取列数 */
        return col;
    }
    long get_row() {  /* 获取行数 */
        return row;
    }
    bool has_same_shape(Matrx<T>& other) {  /* 两个矩阵是否具有相同形状 */
        return other.get_col() != this->get_col()
         || other.get_row() != this->get_row();
    }
    bool has_dot_shape(Matrx<T>& other) { /* 具有矩阵乘法的形状 */
        return this->get_col() == other.get_row();
    }
    void assert_same_shape(Matrx<T>& other) { /* 断言两个矩阵具有相同形状 */
        if (!has_same_shape(other))
            throw std::invalid_argument("The shapes of matrix are not same.");   
    }
    void assert_dot_shape(Matrx<T>& other) { /* 断言两个矩阵具有点积前置形状 */
        if (!has_dot_shape(other))
            throw std::invalid_argument("The shapes of matrix can not be dot.");   
    }
    Matrx<T> operator+(Matrx<T>& other) {  /* 加法操作符重定义 - 矩阵加法 */
        assert_same_shape(other);  /* 检查两个矩阵形状是否一致 */
        auto matrix = *this;
        for (auto it = matrix.get_data().begin(), it_or = other.get_data().begin(); it != this->get_data().end(); ++it, ++it_or ) {
            *it += *it_or;
        }
        return matrix;
    }
    Matrx<T> operator+(T& item) {  /* 矩阵加法广播 */
        auto matrix = *this;
        for (auto it = this->get_data().begin(); it != this->get_data().end(); ++it) {
            *it += item;
        }
        return matrix;
    }
    Matrx<T> operator-(Matrx<T>& other) {  /* 操作符重载 - 矩阵减法 */
        assert_same_shape(other);  /**/
        auto matrix = *this;
        for (auto it = matrix.get_data().begin(), it_or = other.get_data().begin(); it != this->get_data().end(); ++it, ++it_or ) {
            *it -= *it_or;
        }
        return matrix;
    }
    Matrx<T> operator-(T& item) {  /* 操作符重载 - 矩阵减法广播 */
        auto matrix = *this;
        for (auto it = this->get_data().begin(); it != this->get_data().end(); ++it) {
            *it -= item;
        }
        return matrix;
    }
    Matrx<T> operator*(const Matrx<T>& other) {  /* 操作符重载 - 矩阵乘法 */ 
        assert_dot_shape(other); /* 它们的形状是否能够点积 */
        auto matrix = Matrx<T>(this->get_row() * other.get_col());
        for (long i=0; i<this->get_row(); ++i) {/* 点积运算 */
            for (long j=0; j<other.get_col(); ++j) {
                T sum{0};
                for (long k=0; k<this->get_col(); ++k) {
                    for (long o=0; o < other.get_row(); ++o) {
                        sum += this->get(k, i) * other.get(j, o);
                    }
                }
                matrix.get(i, j) = sum;
            }
        }
        return matrix;
    }
    Matrx<T> operator*(T& item) {  /* 操作符重载 - 矩阵乘法广播 */
        auto matrix = *this;
        for (auto it = this->get_data().begin(); it != this->get_data().end(); ++it) {
            *it *= item;
        }
        return matrix;
    }
    Matrx<T> operator/(T& item) {  /* 操作符重载 - 矩阵除法广播 */
        auto matrix = *this;
        for (auto it = this->get_data().begin(); it != this->get_data().end(); ++it) {
            *it /= item;
        }
        return matrix;
    }
private:
    long col, row;
    std::vector<T> arr;  /* 存储矩阵的张量 */
};
}